import mui from './mui'
(function ($) {
  var CLASS_PULL_TOP_TIPS = $.className('pull-top-tips')

  $.PullToRefresh = $.PullToRefresh.extend({
    init: function (element, options) {
      this._super(element, options)
      this.options = $.extend(true, {
        down: {
          tips: {
            colors: ['008000', 'd8ad44', 'd00324', 'dc00b8', '017efc'],
            size: 200, // width=height=size;x=y=size/2;radius=size/4
            lineWidth: 15,
            duration: 1000,
            tail_duration: 1000 * 2.5
          }
        }
      }, this.options)
      this.options.down.tips.color = this.options.down.tips.colors[0]
      this.options.down.tips.colors = this.options.down.tips.colors.map(function (color) {
        return {
          r: parseInt(color.substring(0, 2), 16),
          g: parseInt(color.substring(2, 4), 16),
          b: parseInt(color.substring(4, 6), 16)
        }
      })
    },
    initPullDownTips: function () {
      var self = this
      if ($.isFunction(self.options.down.callback)) {
        self.pullDownTips = (function () {
          var element = document.querySelector('.' + CLASS_PULL_TOP_TIPS)
          if (element) {
            element.parentNode.removeChild(element)
          }
          if (!element) {
            element = document.createElement('div')
            element.classList.add(CLASS_PULL_TOP_TIPS)
            element.innerHTML = '<div class="mui-pull-top-wrapper"><div class="mui-pull-top-canvas"><canvas id="pullDownTips" width="' + self.options.down.tips.size + '" height="' + self.options.down.tips.size + '"></canvas></div></div>'
            element.addEventListener('webkitTransitionEnd', self)
            document.body.appendChild(element)
          }
          self.pullDownCanvas = document.getElementById('pullDownTips')
          self.pullDownCanvasCtx = self.pullDownCanvas.getContext('2d')
          self.canvasUtils.init(self.pullDownCanvas, self.options.down.tips)
          return element
        }())
      }
    },
    removePullDownTips: function () {
      this._super()
      this.canvasUtils.stopSpin()
    },
    pulling: function (deltaY) {
      var ratio = Math.min(deltaY / (this.options.down.height * 1.5), 1)
      var ratioPI = Math.min(1, ratio * 2)
      this.pullDownTips.style.webkitTransform = 'translate3d(0,' + (deltaY < 0 ? 0 : deltaY) + 'px,0)'
      this.pullDownCanvas.style.opacity = ratioPI
      this.pullDownCanvas.style.webkitTransform = 'rotate(' + 300 * ratio + 'deg)'
      var canvas = this.pullDownCanvas
      var ctx = this.pullDownCanvasCtx
      var size = this.options.down.tips.size
      ctx.lineWidth = this.options.down.tips.lineWidth
      ctx.fillStyle = '#' + this.options.down.tips.color
      ctx.strokeStyle = '#' + this.options.down.tips.color
      ctx.stroke()
      ctx.clearRect(0, 0, size, size)
      // fixed android 4.1.x
      canvas.style.display = 'none' // Detach from DOM
      canvas.offsetHeight // Force the detach
      canvas.style.display = 'inherit' // Reattach to DOM
      this.canvasUtils.drawArcedArrow(ctx, size / 2 + 0.5, size / 2, size / 4, 0 * Math.PI, 5 / 3 * Math.PI * ratioPI, false, 1, 2, 0.7853981633974483, 25, this.options.down.tips.lineWidth, this.options.down.tips.lineWidth)
    },

    beforeChangeOffset: function (deltaY) {},
    afterChangeOffset: function (deltaY) {},
    dragEndAfterChangeOffset: function (isNeedRefresh) {
      if (isNeedRefresh) {
        this.canvasUtils.startSpin()
        this.pullDownLoading()
      } else {
        this.canvasUtils.stopSpin()
        this.endPullDownToRefresh()
      }
    },
    canvasUtils: (function () {
      var canvasObj = null,
        ctx = null,
        size = 200,
        lineWidth = 15,
        tick = 0,
        startTime = 0,
        frameTime = 0,
        timeLast = 0,
        oldStep = 0,
        acc = 0,
        head = 0,
        tail = 180,
        rad = Math.PI / 180,
        duration = 1000,
        tail_duration = 1000 * 2.5,
        colors = ['35ad0e', 'd8ad44', 'd00324', 'dc00b8', '017efc'],
        rAF = null

      function easeLinear (currentIteration, startValue, changeInValue, totalIterations) {
        return changeInValue * currentIteration / totalIterations + startValue
      }

      function easeInOutQuad (currentIteration, startValue, changeInValue, totalIterations) {
        if ((currentIteration /= totalIterations / 2) < 1) {
          return changeInValue / 2 * currentIteration * currentIteration + startValue
        }
        return -changeInValue / 2 * ((--currentIteration) * (currentIteration - 2) - 1) + startValue
      }

      function minmax (value, v0, v1) {
        var min = Math.min(v0, v1)
        var max = Math.max(v0, v1)
        if (value < min)
          return min
        if (value > max)
          return min
        return value
      }

      var drawHead = function (ctx, x0, y0, x1, y1, x2, y2, style) {
        'use strict'
        if (typeof(x0) == 'string') x0 = parseInt(x0)
        if (typeof(y0) == 'string') y0 = parseInt(y0)
        if (typeof(x1) == 'string') x1 = parseInt(x1)
        if (typeof(y1) == 'string') y1 = parseInt(y1)
        if (typeof(x2) == 'string') x2 = parseInt(x2)
        if (typeof(y2) == 'string') y2 = parseInt(y2)
        var radius = 3
        var twoPI = 2 * Math.PI
        ctx.save()
        ctx.beginPath()
        ctx.moveTo(x0, y0)
        ctx.lineTo(x1, y1)
        ctx.lineTo(x2, y2)
        switch (style) {
          case 0:
            var backdist = Math.sqrt(((x2 - x0) * (x2 - x0)) + ((y2 - y0) * (y2 - y0)))
            ctx.arcTo(x1, y1, x0, y0, .55 * backdist)
            ctx.fill()
            break
          case 1:
            ctx.beginPath()
            ctx.moveTo(x0, y0)
            ctx.lineTo(x1, y1)
            ctx.lineTo(x2, y2)
            ctx.lineTo(x0, y0)
            ctx.fill()
            break
          case 2:
            ctx.stroke()
            break
          case 3:
            var cpx = (x0 + x1 + x2) / 3
            var cpy = (y0 + y1 + y2) / 3
            ctx.quadraticCurveTo(cpx, cpy, x0, y0)
            ctx.fill()
            break
          case 4:
            var cp1x, cp1y, cp2x, cp2y, backdist
            var shiftamt = 5
            if (x2 == x0) {
              backdist = y2 - y0
              cp1x = (x1 + x0) / 2
              cp2x = (x1 + x0) / 2
              cp1y = y1 + backdist / shiftamt
              cp2y = y1 - backdist / shiftamt
            } else {
              backdist = Math.sqrt(((x2 - x0) * (x2 - x0)) + ((y2 - y0) * (y2 - y0)))
              var xback = (x0 + x2) / 2
              var yback = (y0 + y2) / 2
              var xmid = (xback + x1) / 2
              var ymid = (yback + y1) / 2
              var m = (y2 - y0) / (x2 - x0)
              var dx = (backdist / (2 * Math.sqrt(m * m + 1))) / shiftamt
              var dy = m * dx
              cp1x = xmid - dx
              cp1y = ymid - dy
              cp2x = xmid + dx
              cp2y = ymid + dy
            }
            ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x0, y0)
            ctx.fill()
            break
        }
        ctx.restore()
      }
      var drawArcedArrow = function (ctx, x, y, r, startangle, endangle, anticlockwise, style, which, angle, d, lineWidth, lineRatio) {
        'use strict'
        style = typeof(style) != 'undefined' ? style : 3
        which = typeof(which) != 'undefined' ? which : 1
        angle = typeof(angle) != 'undefined' ? angle : Math.PI / 8
        lineWidth = lineWidth || 1
        lineRatio = lineRatio || 10
        d = typeof(d) != 'undefined' ? d : 10
        ctx.save()
        ctx.lineWidth = lineWidth
        ctx.beginPath()
        ctx.arc(x, y, r, startangle, endangle, anticlockwise)
        ctx.stroke()
        var sx, sy, lineangle, destx, desty
        if (which & 1) {
          sx = Math.cos(startangle) * r + x
          sy = Math.sin(startangle) * r + y
          lineangle = Math.atan2(x - sx, sy - y)
          if (anticlockwise) {
            destx = sx + 10 * Math.cos(lineangle)
            desty = sy + 10 * Math.sin(lineangle)
          } else {
            destx = sx - 10 * Math.cos(lineangle)
            desty = sy - 10 * Math.sin(lineangle)
          }
          drawArrow(ctx, sx, sy, destx, desty, style, 2, angle, d)
        }
        if (which & 2) {
          sx = Math.cos(endangle) * r + x
          sy = Math.sin(endangle) * r + y
          lineangle = Math.atan2(x - sx, sy - y)
          if (anticlockwise) {
            destx = sx - 10 * Math.cos(lineangle)
            desty = sy - 10 * Math.sin(lineangle)
          } else {
            destx = sx + 10 * Math.cos(lineangle)
            desty = sy + 10 * Math.sin(lineangle)
          }
          drawArrow(ctx, sx - lineRatio * Math.sin(endangle), sy + lineRatio * Math.cos(endangle), destx - lineRatio * Math.sin(endangle), desty + lineRatio * Math.cos(endangle), style, 2, angle, d)
        }
        ctx.restore()
      }
      var drawArrow = function (ctx, x1, y1, x2, y2, style, which, angle, d) {
        'use strict'
        if (typeof(x1) == 'string') x1 = parseInt(x1)
        if (typeof(y1) == 'string') y1 = parseInt(y1)
        if (typeof(x2) == 'string') x2 = parseInt(x2)
        if (typeof(y2) == 'string') y2 = parseInt(y2)
        style = typeof(style) != 'undefined' ? style : 3
        which = typeof(which) != 'undefined' ? which : 1
        angle = typeof(angle) != 'undefined' ? angle : Math.PI / 8
        d = typeof(d) != 'undefined' ? d : 10
        var toDrawHead = typeof(style) != 'function' ? drawHead : style
        var dist = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
        var ratio = (dist - d / 3) / dist
        var tox, toy, fromx, fromy
        if (which & 1) {
          tox = Math.round(x1 + (x2 - x1) * ratio)
          toy = Math.round(y1 + (y2 - y1) * ratio)
        } else {
          tox = x2
          toy = y2
        }
        if (which & 2) {
          fromx = x1 + (x2 - x1) * (1 - ratio)
          fromy = y1 + (y2 - y1) * (1 - ratio)
        } else {
          fromx = x1
          fromy = y1
        }
        ctx.beginPath()
        ctx.moveTo(fromx, fromy)
        ctx.lineTo(tox, toy)
        ctx.stroke()
        var lineangle = Math.atan2(y2 - y1, x2 - x1)
        var h = Math.abs(d / Math.cos(angle))
        if (which & 1) {
          var angle1 = lineangle + Math.PI + angle
          var topx = x2 + Math.cos(angle1) * h
          var topy = y2 + Math.sin(angle1) * h
          var angle2 = lineangle + Math.PI - angle
          var botx = x2 + Math.cos(angle2) * h
          var boty = y2 + Math.sin(angle2) * h
          toDrawHead(ctx, topx, topy, x2, y2, botx, boty, style)
        }
        if (which & 2) {
          var angle1 = lineangle + angle
          var topx = x1 + Math.cos(angle1) * h
          var topy = y1 + Math.sin(angle1) * h
          var angle2 = lineangle - angle
          var botx = x1 + Math.cos(angle2) * h
          var boty = y1 + Math.sin(angle2) * h
          toDrawHead(ctx, topx, topy, x1, y1, botx, boty, style)
        }
      }

      var spinColors = function (currentIteration, totalIterations) {
        var step = currentIteration % totalIterations
        if (step < oldStep)
          colors.push(colors.shift())
        var c0 = colors[0],
          c1 = colors[1],
          r = minmax(easeLinear(step, c0.r, c1.r - c0.r, totalIterations), c0.r, c1.r),
          g = minmax(easeLinear(step, c0.g, c1.g - c0.g, totalIterations), c0.g, c1.g),
          b = minmax(easeLinear(step, c0.b, c1.b - c0.b, totalIterations), c0.b, c1.b)

        oldStep = step
        return 'rgb(' + parseInt(r) + ',' + parseInt(g) + ',' + parseInt(b) + ')'
      }

      var spin = function (t) {
        var timeCurrent = t || (new Date).getTime()
        if (!startTime) {
          startTime = timeCurrent
        }
        tick = timeCurrent - startTime
        acc = easeInOutQuad((tick + tail_duration / 2) % tail_duration, 0, duration, tail_duration)
        head = easeLinear((tick + acc) % duration, 0, 360, duration)
        tail = 20 + Math.abs(easeLinear((tick + tail_duration / 2) % tail_duration, -300, 600, tail_duration))

        ctx.lineWidth = lineWidth
        ctx.lineCap = 'round'

        ctx.strokeStyle = spinColors(tick, duration)
        ctx.clearRect(0, 0, size, size)
        //fixed android 4.1.x
        canvasObj.style.display = 'none' // Detach from DOM
        canvasObj.offsetHeight // Force the detach
        canvasObj.style.display = 'inherit' // Reattach to DOM
        ctx.beginPath()
        ctx.arc(size / 2, size / 2, size / 4, parseInt(head - tail) % 360 * rad, parseInt(head) % 360 * rad, false)
        ctx.stroke()

        rAF = requestAnimationFrame(spin)
      }
      var startSpin = function () {
        startTime = 0
        oldStep = 0
        rAF = requestAnimationFrame(spin)
      }
      var stopSpin = function () {
        rAF && cancelAnimationFrame(rAF)
      }
      var init = function (canvas, options) {
        canvasObj = canvas
        ctx = canvasObj.getContext('2d')
        var options = $.extend(true, {}, options)
        colors = options.colors
        duration = options.duration
        tail_duration = options.tail_duration
        size = options.size
        lineWidth = options.lineWidth
      }
      return {
        init: init,
        drawArcedArrow: drawArcedArrow,
        startSpin: startSpin,
        stopSpin: stopSpin
      }
    })()
  })
})(mui)
